36. Solution: Sunshine Content Provider and Query

Setup Sunshine's Content Provider and Query Solution

In this exercise you modified the WeatherProvider class so that it could perform a query. This required registering the content provider in the AndroidManifest.xml, creating a URIMatcher and finally completing the query.

Notes on Solution Code

Add the Content Provider to the Manifest

First, we add the content provider to the manifest, using a provider tag:

<provider
    android:name=".data.WeatherProvider"
    android:authorities="@string/content_authority"
    android:exported="false"/>

Setup the URIMatcher

It's important to note that a lot of code was provided for you. The WeatherContract was updated to include the new URIs you needed for this exercise, namely:

  • content://com.example.android.sunshine/weather/ - The directory of all weather data. This is the same as the CONTENT_URI for the weather table..
  • content://com.example.android.sunshine/weather/# - A single item of data. The number here is meant to match a date. For these URIs the WeatherProvider includes the buildWeatherUriWithDate method.

The URIMatcher should be set up in such a way that it matches and maps these two types of URI to integer constants.

So first things first, you need to define two integer constants:

public static final int CODE_WEATHER = 100;
public static final int CODE_WEATHER_WITH_DATE = 101;

After this you should write a static method to build the URI matcher.

public static UriMatcher buildUriMatcher() {
    final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    final String authority = WeatherContract.CONTENT_AUTHORITY;
    matcher.addURI(authority, WeatherContract.PATH_WEATHER, CODE_WEATHER);
    matcher.addURI(authority, WeatherContract.PATH_WEATHER + "/#", CODE_WEATHER_WITH_DATE);
    return matcher;
}

Initialize the Content Provider

In this case because the underlying data structure is a SQLite database, you need to make a connection to that database in the onCreate method:

mOpenHelper = new WeatherDbHelper(getContext());

Code Query

To code query, you'll need to use your URI matcher to take the incoming URI and figure out what it is

public Cursor query(@NonNull Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Cursor cursor;
        switch (sUriMatcher.match(uri)) {

            case CODE_WEATHER_WITH_DATE: {
                // Code for querying with a date
                break;
            }
            case CODE_WEATHER: {
                // Code for querying the weather table
                break;
            }
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }

The simpler of the two cases is querying the entire directory of weather, seen below:

cursor = mOpenHelper.getReadableDatabase().query(
                    WeatherContract.WeatherEntry.TABLE_NAME,
                    projection,
                    selection,
                    selectionArgs,
                    null,
                    null,
                    sortOrder);

When you're querying with a date, you can use getLastPathSegment to get the date string, then pass it in as a selection argument. The selection parameter should reference the date column, as seen below:

String normalizedUtcDateString = uri.getLastPathSegment();

String[] selectionArguments = new String[]{normalizedUtcDateString};

cursor = mOpenHelper.getReadableDatabase().query(
        /* Table we are going to query */
        WeatherContract.WeatherEntry.TABLE_NAME,
        projection,
        WeatherContract.WeatherEntry.COLUMN_DATE + " = ? ",
        selectionArguments,
        null,
        null,
        sortOrder);
break;

Finally, it's important to set the notification URI for the cursor. We'll use this later when we implement a class called the CursorLoader.

cursor.setNotificationUri(getContext().getContentResolver(), uri);

See the full list of edits in the solution diff below.

Solution Code

Solution: [S09.01-Solution-ContentProviderFoundation][Diff]